use std::time::Duration;

use log::info;
use rodio::{Decoder, OutputStream, source::Source};
use tokio_cron_scheduler::{Job, JobScheduler, JobSchedulerError};
use async_trait::async_trait;

#[derive(Debug, thiserror::Error)]
pub enum TimerError {
    #[error("{0}")]
    DbError(&'static str),

    #[error(transparent)]
    JobSchedulerError(#[from] JobSchedulerError),

    #[error(transparent)]
    Other(#[from] anyhow::Error),
}


#[async_trait]
pub trait TaskAction: Send + Clone + Sync + 'static {
    type Output;
    async fn execute(&self) -> Result<Self::Output, TimerError>;
}


#[derive(Clone)]
pub enum Trigger {
    Cron(CronTrigger),
    Once(TimestampTrigger),
}

#[derive(Clone)]
pub struct CronTrigger {
    expr: String,
}

#[derive(Clone)]
pub struct TimestampTrigger {
    pub timestamp: u64,
}



fn ring(mp3_path: String, duration_sec: u64) {
    // 创建音频输出流
    let (_stream, stream_handle) = OutputStream::try_default().unwrap();

    // 加载音频文件
    let path = mp3_path;
    let file = std::fs::File::open(path).unwrap();
    let source = Decoder::new_mp3(file).unwrap();

    // 播放音频
    stream_handle.play_raw(source.convert_samples()).unwrap();

    // 保持程序运行，直到音频播放完成
    std::thread::sleep(std::time::Duration::from_secs(duration_sec));
}




pub struct Timer {
    sched: JobScheduler,
}

impl Timer {
    pub async fn init() -> Result<Self, TimerError> {
        let sched = JobScheduler::new().await?;
        Ok(Self{sched})
    }

    pub async fn add_task(&self, trigger: Trigger,  action: impl TaskAction) -> Result<String, TimerError>
    {
        let job = match trigger {
            Trigger::Cron(CronTrigger { expr }) => {
                Job::new_async(expr, move|_uuid, mut _l| {
                    let t = action.clone();
                    Box::pin(async move {
                        t.execute().await.unwrap();
                    })
                })?
            },
            Trigger::Once(TimestampTrigger { timestamp }) => {
                let current_time = std::time::SystemTime::now()
                    .duration_since(std::time::UNIX_EPOCH)
                    .expect("Time went backwards")
                    .as_secs();
                info!("duration: {}", (timestamp - current_time));
                let duration = Duration::from_secs((timestamp - current_time));
                Job::new_one_shot_async(duration, move|_uuid, _l| {
                    let t = action.clone();
                    Box::pin(async move {
                        t.execute().await.unwrap();
                    })
                })?
            },
        };

        let id = self.sched.add(job).await?.to_string();
        Ok(id)
    }

    pub async fn start(&self) -> Result<(), TimerError> {
        self.sched.start().await?;
        Ok(())
    }

    pub async fn remove_task(&self, task_id: &str) -> Result<(), TimerError> {
        Ok(())
    }
}

pub struct TaskRecord {
    pub id: &'static str,
    pub trigger_type: &'static str,
    pub trigger_expr: &'static str,
    pub action_type: &'static str,
    pub action_expr: &'static str,
    pub is_finish: i32,
}


#[async_trait]
pub trait DbHandler {
    async fn load_records(&self) -> Result<Vec<TaskRecord>, TimerError>;

    async fn save_record(&self, record: &TaskRecord) -> Result<(), TimerError>;
}


#[cfg(test)]
mod tests {
    use anyhow::Error;
    use chrono::{FixedOffset, NaiveDateTime};
    use dotenvy::dotenv;
    use tokio::time::sleep;
    use tokio_cron_scheduler::{Job, JobScheduler};

    use super::*;

    fn setup() {
        log4rs::init_file("../log4rs.yaml", Default::default()).unwrap();
        dotenv().ok();
    }


    #[tokio::test]
    async fn test_timer() {
        setup();
        let timer = Timer::init().await.unwrap();

        #[derive(Clone, Copy)]
        struct RingAction{}

        #[async_trait]
        impl TaskAction for RingAction {
            type Output = ();
            async fn execute(&self) -> Result<Self::Output, TimerError> {
                println!("ring ring ring");
                ring("../alarm_clock.mp3".to_string(), 5);
                Ok(())
            }
        }

        let id = timer.add_task(Trigger::Cron(CronTrigger { expr: "1/10 * * * * *".to_string() }), RingAction{}).await.unwrap();
        println!("id: {}", id);

        timer.start().await.unwrap();
        
        sleep(std::time::Duration::from_secs(30)).await;
    }

    #[tokio::test]
    async fn test_timer2() {
        setup();
        let timer = Timer::init().await.unwrap();

        #[derive(Clone, Copy)]
        struct RingAction{}

        #[async_trait]
        impl TaskAction for RingAction {
            type Output = ();
            async fn execute(&self) -> Result<Self::Output, TimerError> {
                info!("ring ring ring");
                ring("../alarm_clock.mp3".to_string(), 5);
                Ok(())
            }
        }

        let datetime_str = "2024-11-15 10:29:00"; // 示例字符串
        let format = "%Y-%m-%d %H:%M:%S"; // 字符串格式
        let naive_datetime = NaiveDateTime::parse_from_str(datetime_str, format).expect("解析失败");
        let sec = naive_datetime.and_local_timezone(FixedOffset::east_opt(8 * 3600).unwrap()).unwrap().timestamp();
        info!("sec: {}", sec);

        let _ = timer.add_task(Trigger::Once(TimestampTrigger { timestamp: sec as u64 }), RingAction{}).await;

        timer.start().await.unwrap();

        sleep(std::time::Duration::from_secs(180)).await;
    }

    #[test]
    fn test_datetime() {
        let datetime_str = "2024-11-01 20:10:00"; // 示例字符串
        let format = "%Y-%m-%d %H:%M:%S"; // 字符串格式
        let naive_datetime = NaiveDateTime::parse_from_str(datetime_str, format).expect("解析失败");

        let timestamp = naive_datetime.and_utc().timestamp();
        println!("timestamp: {}", timestamp);

        let current_time = std::time::SystemTime::now()
                    .duration_since(std::time::UNIX_EPOCH)
                    .expect("Time went backwards")
                    .as_secs();
        let duration = Duration::from_secs(timestamp as u64 - current_time);
        println!("duration: {:?}", duration);
    }

    #[tokio::test]
    async fn test_timer4() -> Result<(), Error> {
        let sched = JobScheduler::new().await?;
        sched.start().await?;

        sched.add(
            Job::new_async("1/7 * * * * *", |uuid, mut l| {
                Box::pin(async move {
                    println!("I run async every 7 seconds");
    
                    // Query the next execution time for this job
                    let next_tick = l.next_tick_for_job(uuid).await;
                    match next_tick {
                        Ok(Some(ts)) => println!("Next time for 7s job is {:?}", ts),
                        _ => println!("Could not get next tick for 7s job"),
                    }
                })
            })?
        ).await?;

        sleep(std::time::Duration::from_secs(30)).await;
        Ok(())
    }

    #[tokio::test]
    async fn test_timer3() -> Result<(), Error> {
        let sched = JobScheduler::new().await?;
        sched.start().await?;

        sched.add(
            Job::new("1/10 * * * * *", |_uuid, _l| {
                println!("I run every 10 seconds");
            })?
        ).await?;

        sched.add(
            Job::new_async("1/7 * * * * *", |uuid, mut l| {
                Box::pin(async move {
                    println!("I run async every 7 seconds");
    
                    // Query the next execution time for this job
                    let next_tick = l.next_tick_for_job(uuid).await;
                    match next_tick {
                        Ok(Some(ts)) => println!("Next time for 7s job is {:?}", ts),
                        _ => println!("Could not get next tick for 7s job"),
                    }
                })
            })?
        ).await?;


        sched.add(
            Job::new("1/10 * * * * *", |_uuid, _l| {
                println!("888888");
            })?
        ).await?;

        sleep(std::time::Duration::from_secs(30)).await;
        Ok(())
    }

}